#pragma once

#include "base.h"
#include "../impl/m4.h"

namespace mdtx::online::updater
{
    template <typename T>
    class m4_raw : public updater_base<T, T, 1, 4>,
                   public impl::m4<T>
    {
        using base = updater_base<T, T, 1, 4>;
        using ibase = impl::m4<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;
        using ibase::roll_impl;

        void insert(tuple_in const &in) override
        {
            T x = std::get<0>(in);
            T x2 = x * x;
            T x3 = x2 * x;
            T x4 = x2 * x2;
            insert_impl(x, x2, x3, x4);
        }

        void remove(tuple_in const &in) override
        {
            T x = std::get<0>(in);
            T x2 = x * x;
            T x3 = x2 * x;
            T x4 = x2 * x2;
            remove_impl(x, x2, x3, x4);
        }

        void roll(T x, T xo)
        {
            T x2 = x * x;
            T x3 = x2 * x;
            T x4 = x2 * x2;
            T xo2 = xo * xo;
            T xo3 = xo2 * xo;
            T xo4 = xo2 * xo2;
            roll_impl(x, x2, x3, x4, xo, xo2, xo3, xo4);
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return mk_tuple(this->raw);
        }
    };

    template <typename T>
    class m4_center : public m4_raw<T>
    {
        using base = updater_base<T, T, 1, 4>;

    public:
        using typename base::tuple_out;

        tuple_out get() override
        {
            return mk_tuple(this->center_impl());
        }
    };
}
